package com.ruoyi.web.utils;

import com.github.wxpay.sdk.WXPayConfig;
import com.github.wxpay.sdk.WXPayConstants;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.SchemePortResolver;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 封装http访问方法
 * @author zdh
 * @date 2020-04-07
 */
@Component("httpClientManager")
public class HttpClientManager implements BeanFactoryAware {

    private BeanFactory beanFactory;

    @Resource
    private RequestConfig requestConfig;

    /**
     * @param beanFactory BeanFactory
     * @throws BeansException BeansException
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    /**
     * 获取项目中ColsebleHttpClient对象
     * @return
     */
    private CloseableHttpClient getHttpClient() {
        return this.beanFactory.getBean(CloseableHttpClient.class);
    }

    /**
     * 功能：没有参数访问的get请求
     * @param url
     * @return
     * @throws ClientProtocolException
     * @throws IOException
     */
    public HttpResult doGet(String url) throws ClientProtocolException, IOException {
        //创建http Get请求
        HttpGet httpGet = new HttpGet(url);
        //设置请求惨数
        httpGet.setConfig(requestConfig);
        CloseableHttpResponse response = null;
        try {
            //执行请求
            response = this.getHttpClient().execute(httpGet);
            return new HttpResult(response.getStatusLine().getStatusCode(),
                    EntityUtils.toString(response.getEntity(), "UTF-8"));
        }finally {
            //调用完毕关闭
            if(response != null) {response.close();};
        }
    }

    /**
     * 功能：有参数的get请求
     * @param url
     * @param params
     * @return
     * @throws URISyntaxException
     * @throws ClientProtocolException
     * @throws IOException
     */
    public HttpResult doGet(String url , Map<String, String> params) throws URISyntaxException, ClientProtocolException, IOException {
        URIBuilder uriBuilder = new URIBuilder(url);
        if(params != null) {
            for(String key : params.keySet()) {
                uriBuilder.setParameter(key, params.get(key));
            }
        }
        return this.doGet(uriBuilder.build().toString());
    }

    /**
     * 功能：执行 post请求
     * @param httpPost
     * @return HttpResult
     * @throws ClientProtocolException IOException
     * @throws IOException
     */
    private HttpResult postExecute(HttpPost httpPost) throws ClientProtocolException, IOException {
        CloseableHttpResponse response = null;
        try {
            response = this.getHttpClient().execute(httpPost);
            if (response.getEntity() != null){
                return new HttpResult(response.getStatusLine().getStatusCode(),EntityUtils.toString(response.getEntity(), "UTF-8"));
            }
            return new HttpResult(response.getStatusLine().getStatusCode(),"");
        }finally {
            if(response != null) { response.close();}
        }
    }

    /**
     * 功能：执行 request请求
     * @param request
     * @return HttpResult
     * @throws ClientProtocolException IOException
     * @throws IOException
     */
    private HttpResult execute(HttpUriRequest request) throws ClientProtocolException, IOException {
        CloseableHttpResponse response = null;
        try {
            response = this.getHttpClient().execute(request);
            if (response.getEntity() != null){
                return new HttpResult(response.getStatusLine().getStatusCode(),EntityUtils.toString(response.getEntity(), "UTF-8"));
            }
            return new HttpResult(response.getStatusLine().getStatusCode(),"");
        }finally {
            if(response != null) { response.close();}
        }
    }


    /**
     * 功能：post 无参请求
     * @param url
     * @return
     * @throws IOException
     * @throws ClientProtocolException
     */
    public HttpResult doPost(String url) throws ClientProtocolException, IOException {
        return this.doPost(url,null);
    }

    /**
     * 功能：post 有参请求
     * @param url
     * @param params
     * @return
     * @throws IOException
     * @throws ClientProtocolException
     */
    public HttpResult doPost(String url ,Map<String, String> params) throws ClientProtocolException, IOException {
        //创建http Post请求
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        if(params != null) {
            List<NameValuePair> parameters = new ArrayList<NameValuePair>();
            for (String key : params.keySet()) {
                parameters.add(new BasicNameValuePair(key, params.get(key)));
            }
            //构造一个form表单式的实体
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters);
            //将请求实体类设置到httpPost对象中
            httpPost.setEntity(formEntity);
        }
        return postExecute(httpPost);
    }

    /**
     * 功能：post 有参数，json类型
     * @param url
     * @param json
     * @return
     * @throws ClientProtocolException
     * @throws IOException
     */
    public HttpResult doPostJson(String url ,String json, String mchId) throws ClientProtocolException, IOException {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        httpPost.setHeader("Content-Type", "application/json; charset=UTF-8");
        httpPost.addHeader("Accept", "*/*");
        //标识出传递的参数是application/json
        StringEntity stringEntity = new StringEntity(json , ContentType.APPLICATION_JSON);
        httpPost.setEntity(stringEntity);
        return postExecute(httpPost);
    }

    /**
     * 微信API v3 post Json 访问
     * @param url 访问地址
     * @param json json参数
     * @param mchId User-Agent 访问商户号   （微信api v3 可能会不接受 没有User-Agent请求头的用户请求）
     * @param authorizationInfo 认证信息
     * @return
     * @throws IOException
     */
    public HttpResult WeChatPostJson(String url, String json, String mchId, String authorizationInfo) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        httpPost.setHeader("Content-Type", "application/json; charset=UTF-8");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("User-Agent", WXPayConstants.USER_AGENT + " " + mchId);
        httpPost.addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 " + authorizationInfo);
        StringEntity stringEntity = new StringEntity(json , ContentType.APPLICATION_JSON);
        httpPost.setEntity(stringEntity);
        return postExecute(httpPost);
    }

    /**
     * 微信API v3 put  访问
     * @param url 访问地址
     * @param json json参数
     * @param mchId User-Agent 访问商户号   （微信api v3 可能会不接受 没有User-Agent请求头的用户请求）
     * @param authorizationInfo 认证信息
     * @return
     * @throws IOException
     */
    public HttpResult WeChatPutJson(String url, String json, String mchId, String authorizationInfo) throws IOException {
        HttpPut httpPut = new HttpPut(url);
        httpPut.setConfig(requestConfig);
        httpPut.setHeader("Content-Type", "application/json; charset=UTF-8");
        httpPut.addHeader("Accept", "application/json");
        httpPut.addHeader("User-Agent", WXPayConstants.USER_AGENT + " " + mchId);
        httpPut.addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 " + authorizationInfo);
        StringEntity stringEntity = new StringEntity(json , ContentType.APPLICATION_JSON);
        httpPut.setEntity(stringEntity);
        return execute(httpPut);
    }

     /**
     * 微信API v3 delete  访问
     * @param url 访问地址
     * @param mchId User-Agent 访问商户号   （微信api v3 可能会不接受 没有User-Agent请求头的用户请求）
     * @param authorizationInfo 认证信息
     * @return
     * @throws IOException
     */
    public HttpResult WeChatDelete(String url, String mchId, String authorizationInfo) throws IOException {
        HttpDelete httpDelete = new HttpDelete(url);
        httpDelete.setConfig(requestConfig);
        httpDelete.setHeader("Content-Type", "application/json; charset=UTF-8");
        httpDelete.addHeader("Accept", "application/json");
        httpDelete.addHeader("User-Agent", WXPayConstants.USER_AGENT + " " + mchId);
        httpDelete.addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 " + authorizationInfo);
        CloseableHttpResponse response = null;
        return execute(httpDelete);
    }



    /**
     * 携带微信验证头格式 --- 微信支付分专用
     * @param url
     * @return
     * @throws IOException
     */
    public HttpResult weChatDoGet(String url, String mchId, String authorizationInfo) throws IOException {
        HttpGet httpGet = new HttpGet(url);
        httpGet.setConfig(requestConfig);
        CloseableHttpResponse response = null;
        try {
            httpGet.addHeader("Accept", "*/*");
            httpGet.addHeader("User-Agent", WXPayConstants.USER_AGENT + " " + mchId);
            httpGet.addHeader("Authorization", "WECHATPAY2-SHA256-RSA2048 " + authorizationInfo);
            response = this.getHttpClient().execute(httpGet);
            return new HttpResult(response.getStatusLine().getStatusCode(),
                    EntityUtils.toString(response.getEntity(), "UTF-8"));
        }finally {
            if(response != null) {response.close();};
        }
    }

    /**
     * 功能：post请求 参数为 xml类型
     * @param url
     * @param xml
     * @return
     * @throws IOException
     */
    public HttpResult doPostXml(String url, String xml , String mchId) throws IOException {
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        httpPost.addHeader("User-Agent", WXPayConstants.USER_AGENT + " " + mchId);
        StringEntity stringEntity = new StringEntity(xml , "UTF-8");
        httpPost.setEntity(stringEntity);
        return postExecute(httpPost);
    }

    /**
     * 微信打款连接，请求携带用户证书SSL信息
     * @param url 地址
     * @param xml 参数
     * @return HttpResult
     */
    public HttpResult weChatRemit(String url, String xml, WXPayConfig wxPayConfig) throws KeyStoreException, IOException, CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyManagementException {
        //写入ssl证书
        char[] password = wxPayConfig.getMchID().toCharArray();
        InputStream certStream = wxPayConfig.getCertStream();
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(certStream, password);
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, password);
        //创建ssl
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(), (TrustManager[])null, new SecureRandom());
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                sslContext,
//                new String[]{"TLSv1"},
                null,
                (String[]) null,
                new DefaultHostnameVerifier());
        BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager(
                                                                RegistryBuilder.<ConnectionSocketFactory>create().
                                                                        register("http", PlainConnectionSocketFactory
                                                                                .getSocketFactory())
                                                                        .register("https", sslConnectionSocketFactory)
                                                                        .build(), (HttpConnectionFactory)null, (SchemePortResolver)null, (DnsResolver)null);
        CloseableHttpClient httpClient = HttpClientBuilder.create().setConnectionManager(connManager).build();
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        httpPost.addHeader("Content-Type", "text/xml; charset=UTF-8");
        httpPost.addHeader("X-Requested-With", "XMLHttpRequest");
        httpPost.addHeader("User-Agent", WXPayConstants.USER_AGENT + " " + wxPayConfig.getMchID());
        StringEntity stringEntity = new StringEntity(xml , "UTF-8");
        httpPost.setEntity(stringEntity);
        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpPost);
            return new HttpResult(response.getStatusLine().getStatusCode(),
                    EntityUtils.toString(response.getEntity(), "UTF-8"));
        }finally {
            if(response != null) { response.close();};
        }
    }


}
